function validPathPart (p, keepBlanks) {
  return typeof p === "string" && (p || keepBlanks);
}


exports.join = function () {
  var args = Array.prototype.slice.call(arguments);
  // edge case flag to switch into url-resolve-mode
  var keepBlanks = false;
  if (args[ args.length - 1 ] === true) {
    keepBlanks = args.pop();
  }
  // return exports.split(args.join("/"), keepBlanks).join("/");
  var joined = exports.normalizeArray(args, keepBlanks).join("/");
  return joined;
};


exports.split = function (path, keepBlanks) {
  // split based on /, but only if that / is not at the start or end.
  return exports.normalizeArray(path.split(/^|\/(?!$)/), keepBlanks);
};


function cleanArray (parts, keepBlanks) {
  var i = 0;
  var l = parts.length - 1;
  var stripped = false;

  // strip leading empty args
  while (i < l && !validPathPart(parts[i], keepBlanks)) {
    stripped = true;
    i ++;
  }

  // strip tailing empty args
  while (l >= i && !validPathPart(parts[l], keepBlanks)) {
    stripped = true;
    l --;
  }

  if (stripped) {
    // if l chopped all the way back to i, then this is empty
    parts = Array.prototype.slice.call(parts, i, l + 1);
  }

  return parts.filter(function (p) { return validPathPart(p, keepBlanks) })
              .join('/')
              .split(/^|\/(?!$)/);
}


exports.normalizeArray = function (original, keepBlanks) {
  var parts = cleanArray(original, keepBlanks);
  if (!parts.length || (parts.length === 1 && !parts[0])) return ["."];

  // now we're fully ready to rock.
  // leading/trailing invalids have been stripped off.
  // if it comes in starting with a slash, or ending with a slash,
  var leadingSlash = (parts[0].charAt(0) === "/");

  if (leadingSlash) parts[0] = parts[0].substr(1);
  var last = parts.slice(-1)[0];
  var tailingSlash = (last.substr(-1) === "/");
  if (tailingSlash) parts[parts.length - 1] = last.slice(0, -1);
  var directories = [];
  var prev;
  for (var i = 0, l = parts.length - 1 ; i <= l ; i ++) {
    var directory = parts[i];

    // if it's blank, and we're not keeping blanks, then skip it.
    if (directory === "" && !keepBlanks) continue;

    // if it's a dot, then skip it
    if (directory === "." && (
      directories.length
      || (i === 0 && !(tailingSlash && i === l))
      || (i === 0 && leadingSlash)
    )) continue;

    // if we're dealing with an absolute path, then discard ..s that go
    // above that the base.
    if (leadingSlash && directories.length === 0 && directory === "..") {
      continue;
    }
    // trying to go up a dir
    if (
      directory === ".."
      && directories.length
      && prev !== ".."
      && prev !== undefined
    ) {
      directories.pop();
      prev = directories.slice(-1)[0];
    } else {
      directories.push(directory);
      prev = directory;
    }
  }
  if (!directories.length) {
    directories = [ leadingSlash || tailingSlash ? "" : "." ];
  }
  var last = directories.slice(-1)[0];
  if (tailingSlash && last.substr(-1) !== "/") {
    directories[directories.length-1] += "/";
  }
  if (leadingSlash && directories[0].charAt(0) !== "/") {
    if (directories[0] === ".") directories[0] = "";
    directories[0] = "/" + directories[0];
  }
  return directories;
};


exports.normalize = function (path, keepBlanks) {
  return exports.join(path, keepBlanks || false);
};


exports.dirname = function (path) {
  if (path.length > 1 && '/' === path[path.length-1]) {
    path = path.replace(/\/+$/, '');
  }
  var lastSlash = path.lastIndexOf('/');
  switch (lastSlash) {
    case -1:
      return '.';
    case 0:
      return '/';
    default:
      return path.substring(0, lastSlash);
  }
};


exports.basename = function (path, ext) {
  var f = path.substr(path.lastIndexOf("/") + 1);
  if (ext && f.substr(-1 * ext.length) === ext) {
    f = f.substr(0, f.length - ext.length);
  }
  return f;
};


exports.extname = function (path) {
  var dot = path.lastIndexOf('.'),
      slash = path.lastIndexOf('/');
  // The last dot must be in the last path component, and it (the last dot) must
  // not start the last path component (i.e. be a dot that signifies a hidden
  // file in UNIX).
  return dot <= slash + 1 ? '' : path.substring(dot);
};


exports.exists = function (path, callback) {
  process.binding('fs').stat(path, function (err, stats) {
    if (callback) callback(err ? false : true);
  });
};


exports.existsSync = function (path) {
  try {
    process.binding('fs').stat(path);
    return true;
  } catch(e){
    return false;
  }
};
